import { message } from 'antd';

export interface WebSocketConfig {
  url: string;
  reconnectInterval?: number;
  maxReconnectAttempts?: number;
  heartbeatInterval?: number;
  maxReconnectDelay?: number;
  reconnectBackoffMultiplier?: number;
}

export class WebSocketService {
  private ws: WebSocket | null = null;
  private config: Required<WebSocketConfig>;
  private reconnectAttempts = 0;
  private reconnectTimer: NodeJS.Timeout | null = null;
  private heartbeatTimer: NodeJS.Timeout | null = null;
  private messageHandlers: Map<string, Function[]> = new Map();
  private isIntentionallyClosed = false;
  private currentReconnectDelay = 0;

  constructor(config: WebSocketConfig) {
    this.config = {
      url: config.url,
      reconnectInterval: config.reconnectInterval || 1000,
      maxReconnectAttempts: config.maxReconnectAttempts || 10,
      heartbeatInterval: config.heartbeatInterval || 30000,
      maxReconnectDelay: config.maxReconnectDelay || 30000,
      reconnectBackoffMultiplier: config.reconnectBackoffMultiplier || 1.5,
    };
    this.currentReconnectDelay = this.config.reconnectInterval;
  }

  connect(): Promise<void> {
    return new Promise((resolve, reject) => {
      try {
        this.isIntentionallyClosed = false;
        this.ws = new WebSocket(this.config.url);

        this.ws.onopen = () => {
          console.log('WebSocket connected to:', this.config.url);
          this.reconnectAttempts = 0;
          this.currentReconnectDelay = this.config.reconnectInterval;
          this.startHeartbeat();
          this.emit('connected', null);
          resolve();
        };

        this.ws.onmessage = (event) => {
          try {
            const data = JSON.parse(event.data);
            this.handleMessage(data);
          } catch (error) {
            console.error('Failed to parse WebSocket message:', error);
          }
        };

        this.ws.onerror = (error) => {
          console.error('WebSocket error:', error);
          this.emit('error', error);
          reject(error);
        };

        this.ws.onclose = (event) => {
          console.log('WebSocket closed:', event.code, event.reason);
          this.stopHeartbeat();
          this.emit('disconnected', event);
          
          if (!this.isIntentionallyClosed) {
            this.attemptReconnect();
          }
        };
      } catch (error) {
        reject(error);
      }
    });
  }

  disconnect(): void {
    this.isIntentionallyClosed = true;
    this.stopHeartbeat();
    
    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer);
      this.reconnectTimer = null;
    }
    
    if (this.ws) {
      this.ws.close(1000, 'Client disconnect');
      this.ws = null;
    }
  }

  send(type: string, data: any): void {
    if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
      console.error('WebSocket is not connected');
      return;
    }

    const message = JSON.stringify({ type, data, timestamp: Date.now() });
    this.ws.send(message);
  }

  on(event: string, handler: Function): void {
    if (!this.messageHandlers.has(event)) {
      this.messageHandlers.set(event, []);
    }
    this.messageHandlers.get(event)?.push(handler);
  }

  off(event: string, handler?: Function): void {
    if (!handler) {
      this.messageHandlers.delete(event);
    } else {
      const handlers = this.messageHandlers.get(event);
      if (handlers) {
        const index = handlers.indexOf(handler);
        if (index > -1) {
          handlers.splice(index, 1);
        }
      }
    }
  }

  private emit(event: string, data: any): void {
    const handlers = this.messageHandlers.get(event);
    if (handlers) {
      handlers.forEach(handler => handler(data));
    }
  }

  private handleMessage(message: any): void {
    // Handle special message types
    if (message.type === 'pong') {
      // Heartbeat response
      return;
    }

    // Emit message to registered handlers
    this.emit('message', message);
    
    // Emit specific message type
    if (message.type) {
      this.emit(message.type, message.data);
    }
  }

  private startHeartbeat(): void {
    this.stopHeartbeat();
    this.heartbeatTimer = setInterval(() => {
      if (this.ws && this.ws.readyState === WebSocket.OPEN) {
        this.send('ping', null);
      }
    }, this.config.heartbeatInterval);
  }

  private stopHeartbeat(): void {
    if (this.heartbeatTimer) {
      clearInterval(this.heartbeatTimer);
      this.heartbeatTimer = null;
    }
  }

  private attemptReconnect(): void {
    if (this.reconnectAttempts >= this.config.maxReconnectAttempts) {
      console.error('Max reconnection attempts reached');
      message.error('无法连接到实时更新服务，请刷新页面重试');
      this.currentReconnectDelay = this.config.reconnectInterval;
      return;
    }

    this.reconnectAttempts++;
    
    // Calculate exponential backoff delay
    const jitter = Math.random() * 1000; // Add jitter to prevent thundering herd
    const delay = Math.min(this.currentReconnectDelay + jitter, this.config.maxReconnectDelay);
    
    console.log(`Attempting to reconnect (${this.reconnectAttempts}/${this.config.maxReconnectAttempts}) in ${Math.round(delay)}ms...`);
    
    this.reconnectTimer = setTimeout(() => {
      this.connect().catch(error => {
        console.error('Reconnection failed:', error);
        // Increase delay for next attempt using exponential backoff
        this.currentReconnectDelay = Math.min(
          this.currentReconnectDelay * this.config.reconnectBackoffMultiplier,
          this.config.maxReconnectDelay
        );
      });
    }, delay);
  }

  getState(): number {
    return this.ws?.readyState || WebSocket.CLOSED;
  }

  isConnected(): boolean {
    return this.ws?.readyState === WebSocket.OPEN;
  }
}

// Create a singleton instance for hot radar WebSocket
let hotRadarWebSocket: WebSocketService | null = null;

export const getHotRadarWebSocket = (): WebSocketService => {
  if (!hotRadarWebSocket) {
    const wsUrl = import.meta.env.VITE_WS_URL || 'ws://localhost:8002';
    hotRadarWebSocket = new WebSocketService({
      url: `${wsUrl}/ws/hot-items`,
      reconnectInterval: 5000,
      maxReconnectAttempts: 10,
      heartbeatInterval: 30000,
    });
  }
  return hotRadarWebSocket;
};